package cn.chenfeifx.media.controls.skin;

import cn.chenfeifx.core.common.ClipUtil;
import cn.chenfeifx.core.common.TimelineBuilder;
import cn.chenfeifx.media.controls.CFVideoPlayer;
import cn.chenfeifx.media.controls.MediaPlayerSlider;
import cn.chenfeifx.media.controls.common.TimeUtils;
import javafx.animation.Timeline;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.SkinBase;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.scene.paint.Color;
import javafx.util.Duration;
import org.kordamp.ikonli.Ikon;
import org.kordamp.ikonli.boxicons.BoxiconsRegular;
import org.kordamp.ikonli.boxicons.BoxiconsSolid;
import org.kordamp.ikonli.javafx.FontIcon;

import java.util.Optional;
import java.util.concurrent.Callable;

public class CFVideoPlayerSkin extends SkinBase<CFVideoPlayer> {

    private final VideoRegion videoRegion;

    public CFVideoPlayerSkin(CFVideoPlayer control) {
        super(control);
        videoRegion = new VideoRegion();
        control.addEventFilter(Event.ANY, event -> videoRegion.hideBottomNode());
        control.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
            switch (event.getCode()) {
                case SPACE -> videoRegion.playOrPause();
                case RIGHT -> videoRegion.fastForward(Duration.seconds(15)); // 15秒
                case LEFT -> videoRegion.fastForward(Duration.seconds(-15));
                case UP -> videoRegion.setVolume(0.05);
                case DOWN -> videoRegion.setVolume(-0.05);
            }
        });
        getChildren().add(videoRegion);
    }

    /**
     * 视频容器
     */
    public class VideoRegion extends Region {

        private final MediaView mediaView;
        private final MediaPlayerSlider mediaPlayerSlider;
        private final Label timeLabel;
        private final Button rewind = getButton(BoxiconsRegular.REWIND);
        private final Button play = getButton(BoxiconsRegular.PLAY);
        private final Button fastForward = getButton(BoxiconsRegular.FAST_FORWARD);
        private final Button menu = getButton(BoxiconsRegular.MENU); // menu
        //
        private final FontIcon volume = FontIcon.of(BoxiconsSolid.VOLUME_FULL);
        private final MediaPlayerSlider volumeSlider;
        private final HBox volumeBox;
        private final VBox bottomBox;
        //
        private Media media;
        private MediaPlayer mediaPlayer;

        private Button getButton(Ikon ikon) {
            FontIcon fontIcon = FontIcon.of(ikon);
            Button button = new Button("", fontIcon);
            button.setMinSize(USE_PREF_SIZE, USE_PREF_SIZE);
            return button;
        }

        public VideoRegion() {
            mediaView = new MediaView();
            mediaView.setPreserveRatio(true);
            mediaView.setSmooth(true);
            // 媒体视图大小发生变化
            ChangeListener<Bounds> mediaViewSizeChange = (observable, v1, v2) -> {
                Bounds bounds = mediaView.getLayoutBounds();
                mediaView.setLayoutX((getWidth() - bounds.getWidth()) / 2);
                mediaView.setLayoutY((getHeight() - bounds.getHeight()) / 2);
            };
            layoutBoundsProperty().addListener(mediaViewSizeChange);
            mediaView.layoutBoundsProperty().addListener(mediaViewSizeChange);
            mediaView.fitWidthProperty().bind(widthProperty());
            mediaView.fitHeightProperty().bind(heightProperty());
            // 底部上侧滑块
            mediaPlayerSlider = new MediaPlayerSlider();
            mediaPlayerSlider.setArc(0);
            // 滑块值监听
            mediaPlayerSlider.valueProperty().addListener((observable, v1, v2) -> {
                boolean b = Math.abs(mediaPlayer.getCurrentTime().toMillis() - v2.doubleValue()) > 1000; // value值大于1秒
                if (b) {
                    Duration millis = Duration.millis(v2.doubleValue());
                    mediaPlayer.seek(millis);
                }
            });
            // 底部左侧
            timeLabel = new Label("00:00 / 00:00");
            timeLabel.getStyleClass().add("time-label");
            // 底部中间：操作按钮
            HBox buttons = new HBox(rewind, play, fastForward);
            buttons.getStyleClass().add("buttons");
            rewind.getStyleClass().add("rewind");
            play.getStyleClass().add("play");
            fastForward.getStyleClass().add("fast-forward");
            rewind.setOnMouseClicked(event -> fastForward(Duration.seconds(-15)));
            EventHandler<MouseEvent> mouseClicked = event -> playOrPause();
            play.setOnMouseClicked(mouseClicked);
            mediaView.setOnMouseClicked(mouseClicked);
            fastForward.setOnMouseClicked(event -> fastForward(Duration.seconds(15)));
            buttons.setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);
            // 底部右侧
            volumeSlider = new MediaPlayerSlider(0, 1, 0.5);
            volumeSlider.setMode(MediaPlayerSlider.Mode.ANY);
            volume.getStyleClass().add("volume");
            volume.iconCodeProperty().bind(Bindings.createObjectBinding((Callable<Ikon>) ()
                            -> volumeSlider.getValue() == 0 ? BoxiconsSolid.VOLUME_MUTE : BoxiconsSolid.VOLUME_FULL,
                    volumeSlider.valueProperty()));
            volumeBox = new HBox(volume, volumeSlider);
            volumeSlider.setPrefWidth(80);
            volumeBox.getStyleClass().add("volume-box");
            HBox bottomRight = new HBox(volumeBox, menu);
            bottomRight.setMaxSize(USE_PREF_SIZE, USE_COMPUTED_SIZE);
            bottomRight.setAlignment(Pos.CENTER);
            bottomRight.getStyleClass().add("bottom-right");
            // 底部容器
            StackPane bottom = new StackPane(timeLabel, buttons, bottomRight);
            bottom.getStyleClass().add("bottom");
            StackPane.setAlignment(timeLabel, Pos.CENTER_LEFT);
            StackPane.setAlignment(bottomRight, Pos.BOTTOM_RIGHT);
            //
            bottomBox = new VBox(mediaPlayerSlider, bottom);
            bottomBox.getStyleClass().add("bottom-box");
            bottomBox.setMaxHeight(USE_PREF_SIZE);
            bottomBox.prefWidthProperty().bind(widthProperty());
            bottomBox.layoutYProperty().bind(heightProperty().subtract(bottomBox.heightProperty()));
            //
            getChildren().addAll(mediaView, bottomBox);
            ClipUtil.clipRect(this, new SimpleDoubleProperty(0)); // 裁剪
            setBackground(new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY)));
            //
            resetMedia(getSkinnable().getSource());
            getSkinnable().sourceProperty().addListener((observable, oldValue, newValue) -> resetMedia(newValue));
        }

        /**
         * 播放或者暂停
         */
        public void playOrPause() {
            Optional.ofNullable(mediaPlayer).ifPresent(mp -> {
                System.out.println(mp.getStatus());
                switch (mp.getStatus()) {
                    case READY, PAUSED, STOPPED -> mp.play();
                    case PLAYING -> mp.pause();
                }
            });
        }

        /**
         * 快进
         */
        public void fastForward(Duration dur) {
            Optional.ofNullable(mediaPlayer).ifPresent(mp -> {
                Duration currentTime = mp.getCurrentTime();
                mp.seek(currentTime.add(dur));
            });
        }

        /**
         * 增加音量
         */
        public void setVolume(double add) {
            Optional.ofNullable(mediaPlayer).ifPresent(mp -> {
                double volume = mp.getVolume() + add;
                double v = volume > 1 ? 1 : volume < 0 ? 0 : volume;
                System.out.println(v);
                mp.setVolume(v);
            });
        }

        int errorCount = 0;

        private void resetMedia(String source) {
            if (!source.isBlank()) {
                if (mediaPlayer != null) {
                    mediaPlayer.dispose();
                }
                media = new Media(source);
                mediaPlayer = new MediaPlayer(media);
                mediaView.setMediaPlayer(mediaPlayer);
                //
                mediaPlayer.setOnError(() -> {
                    if (errorCount < 1) {
                        resetMedia(source);
                        System.err.println("媒体加载错误，尝试重新加载。。。");
                    } else {
                        System.err.println(mediaPlayer.getError().toString());
                    }
                    errorCount++;
                });
                mediaPlayer.setOnReady(() -> {
                    errorCount = 0;
                    // ObservableMap<String, Object> metadata = media.getMetadata();
                    mediaPlayer.volumeProperty().bindBidirectional(volumeSlider.valueProperty());
                    mediaPlayerSlider.setNumber(0, media.getDuration().toMillis(), 0);
                    mediaPlayer.currentTimeProperty().addListener((observable, oldValue, newValue) -> {
                        if (!mediaPlayerSlider.isPressed()) {
                            mediaPlayerSlider.setValue(newValue.toMillis());
                        }
                        timeLabel.setText(TimeUtils.getTimeStr(newValue, media.getDuration()));
                    });
                    mediaPlayer.statusProperty().addListener((observable, v1, v2) ->
                            play.setGraphic(FontIcon.of(MediaPlayer.Status.PLAYING.equals(v2) ? BoxiconsRegular.PAUSE : BoxiconsRegular.PLAY)));
                    //
                    timeLabel.setText(TimeUtils.getTimeStr(Duration.ZERO, media.getDuration()));
                    //
                    mediaPlayer.setOnEndOfMedia(() -> mediaPlayer.stop());
                    System.out.println("setOnReady");
                });
            }
        }

        private Timeline hideTimeline;

        /**
         * 隐藏底部节点
         */
        public void hideBottomNode() {
            if (hideTimeline == null) {
                hideTimeline = TimelineBuilder.builder()
                        .addKeyFrame(bottomBox.opacityProperty(), 1, Duration.ZERO, event -> {
                            try {
                                getChildren().add(bottomBox);
                            } catch (Exception ignored) {
                            }
                        })
                        .addKeyFrame(bottomBox.opacityProperty(), 1, Duration.millis(5000))
                        .addKeyFrame(bottomBox.opacityProperty(), 0, Duration.millis(5100), event -> getChildren().remove(bottomBox))
                        .build();
            }
            hideTimeline.stop();
            if (MediaPlayer.Status.PLAYING.equals(
                    Optional.ofNullable(mediaPlayer).map(MediaPlayer::getStatus).orElse(null)
            )) {
                hideTimeline.playFromStart();
            }
        }
    }

}
